<template>
  <div>
    <p>pdf总页码{{ pageNum }}</p>
    <p>当前页码{{ pdfPage }}</p>
    <button @click="handlePrev">上一页</button>
    <button @click="handleNext">下一页</button>
    <button @click="getSignedPdf">确认签署</button>
    <div>
      <img @dragstart="handleDrag(item)" class="imgItem" v-for="item in mainImagelist" :src="item" alt=""
        draggable="true">
    </div>
    <div class="box-con" ref="canvasContRef" @drop="handleDrop" @dragover.prevent>
      <canvas class="pdf-canvas" ref="pdfCanvas"></canvas>
      <div class="stamp-canvas">
        <canvas id="stamp-canvas"></canvas>
      </div>
    </div>
  </div>
</template>

<script>
import { fabric } from 'fabric'
import { PDFDocument } from 'pdf-lib'

let pdfGlobal = require('pdfjs-dist/build/pdf.min.mjs')
let pdfWorker = require('pdfjs-dist/build/pdf.worker.min.mjs')
export default {
  data() {
    return {
      pdfUrl: 'http://lc-G6HU8PIb.cn-n1.lcfile.com/gDazirkqbdIGFlrUHAGfru8K90lx3ipP/pauschLastLectureChineseTranslation_10_25_07.pdf',
      pageNum: 1,
      pdfPage: 1,
      scale: 1,
      pdfDoc: null,
      pageRendering: false,
      fabricCanvas: null,//画布，存放图章拖拽fabric对象
      pageNumPending: null,//正在更新渲染的pdf画布页码
      mainImagelist: [],//图片数组
      stempImgUrl: "",//存放正在绘制的图章路径
      canvasPos: {
        //记录pdf容器位置，用以修正拖拽图章位置
        x: 0,
        y: 0
      },
      stampList: {},//在pdf切页的时候，将已经绘制的图章位置，跟pdf页码关联存储
    }
  },
  async mounted() {
    this.pdfIniit(),
      this.fabricInit(),
      this.canvasPosInit()
  },
  created() {
    this.mainImagelist = [
      require("../assets/img/sign1.png"),
      require("../assets/img/sign2.png"),
      require("../assets/img/sign3.png"),
    ]
  },
  methods: {
    // 确认签署
    async getSignedPdf() {
      const url = this.pdfUrl;
      const existingPdfBytes = await fetch(url).then((res) =>
        res.arrayBuffer()
      );
      // console.log("字节数组",existingPdfBytes)
      const pdfDoc = await PDFDocument.load(existingPdfBytes)//PDFDocument是pdf-lib里面的方法
      console.log("pdfDoc", pdfDoc)
      const pages = pdfDoc.getPages()
      console.log("pages", pages)
      for (let p in this.stampList) {
        let page = pages[p - 1]//有印章的这页pdf对象
        console.log("page", p, page)
        for (let uid in this.stampList[p]) {
          let { imgurl, x, y } = this.stampList[p][uid]//提出印章页pdf的imgurl,x,y
          await this.renderStampOnPdf(pdfDoc, page, { imgurl, x, y })
        }
      }

      const pdfBytes = await pdfDoc.save();
      // 将字节数组保存为文件
      this.saveToFile(pdfBytes, "签署后的文件")
      console.log("签署成功")
    },
    // 将pdf字节数组，存储为pdf文件
    saveToFile(data, filename) {
      // 创建一个 Blob 对象，将二进制数据 data 作为参数传递进去。{type: "application/pdf"} 指定了 Blob 对象的 MIME 类型，这里假设是一个 PDF 文件。
      const blob = new Blob([data], { type: "application/pdf" })
      const link = document.createElement("a")
      //  Blob 对象生成一个临时的 URL,并将url赋值给a标签的herf属性
      link.href = window.URL.createObjectURL(blob);
      link.download = filename;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    },
    // 根据图片路径，获取图片的二进制数组数据（绘制印章的时候需要）
    async getImageDataFromUrl(imageUrl) {
      try {
        const response = await fetch(imageUrl)
        if (!response.ok) {
          throw new Error(`Failed to fetch image: ${response.statusText}`);
        }
        const arrayBuffer = await response.arrayBuffer();
        return new Uint8Array(arrayBuffer)
      } catch (error) {
        console.error("Error fetching image:", error);
        throw error;//将异常重新抛出,确保异常不会被静默处理
      }
    },
    //根据pdf容器位置，等比例换算将印章绘制在pdf源文件对应位置
    async renderStampOnPdf(pdfDoc, pdfPage, { imgurl, x, y }) {
      const containerWidth = 400;//容器的宽度
      const containerHeight = 300;//容器的高度

      const imageXInContainer = x;//图片在容器中的x坐标
      const imageYInContainer = y;//图片在容器中的y坐标
      const imageWidthInContainer = 100;//图片在容器中的宽度
      const imageHeightInContainer = 100;//图片在容器中的高度

      //获取pdf页面的宽度高度
      const pdfPageWidth = pdfPage.getWidth();
      const pdfPageHeight = pdfPage.getHeight();

      // 计算缩放比例(pdf页面中的实际宽度/在容器中的宽度)
      const scaleX = pdfPageWidth / containerWidth;
      const scaleY = pdfPageHeight / containerHeight;
      // 计算在PDF页面上的坐标和尺寸
      const imageXInPdf = imageXInContainer * scaleX;//印章在pdf文件上x坐标的位置=印章在容器中的*缩放的x比例
      const imageYInPdf = pdfPageHeight - imageYInContainer * scaleY; // Y 坐标需要调整，因为 PDF 页面的坐标原点在左下角
      const imageWidthInPdf = imageWidthInContainer * scaleX;
      const imageHeightInPdf = imageHeightInContainer * scaleY;

      // 添加图片到PDF页面
      const imageBytes = await this.getImageDataFromUrl(imgurl)//获取图片二进制数组数据
      const image = await pdfDoc.embedPng(imageBytes)
      pdfPage.drawImage(image, {
        x: imageXInPdf,
        y: imageYInPdf,
        width: imageWidthInPdf,
        height: imageHeightInPdf
      })


    },
    //1.初始化pdf，获取pdf文件，并拿到数据，渲染出第一页pdf
    async pdfIniit() {
      let pdfjsLib = await pdfGlobal
      pdfjsLib.getDocument(this.pdfUrl).promise.then((pdfdoc) => {
        console.log(pdfdoc)
        this.pdfDoc = pdfdoc
        this.pageNum = this.pdfDoc.numPages
        this.renderPage(this.pdfPage)//4.调用渲染页面，传入渲染的页码
      })
    },//3.创建视口，渲染pdf文件
    renderPage(num) {
      let canvas = this.$refs.pdfCanvas;
      let ctx = canvas.getContext("2d");
      this.pageRendering = true;
      this.pdfDoc.getPage(num).then((page) => {
        var viewport = page.getViewport({ scale: this.scale, });
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        var renderContext = {
          canvasContext: ctx,
          viewport: viewport,
        }
        var renderTask = page.render(renderContext);
        renderTask.promise.then(() => {
          console.log('页面渲染了');
          this.pageRendering = false;
          if (this.pageNumPending !== null) {
            this.renderPage(this.pageNumPending);
            this.pageNumPending = null;
          }
        });
      })
    },//2.初始化画布，并绑定元素(此画布是图章层的)
    fabricInit() {
      let w = this.$refs.canvasContRef.clientWidth
      let h = this.$refs.canvasContRef.clientHeight
      // 绑定画布
      this.fabricCanvas = new fabric.Canvas("stamp-canvas", {
        width: w,//设定图章画布宽高
        height: h
      })
    },//获取pdf盒子的x、y
    canvasPosInit() {
      let canvasCont = this.$refs.canvasContRef
      this.canvasPos.x = canvasCont.offsetLeft//元素距离左边缘的偏移量
      this.canvasPos.y = canvasCont.offsetTop
      console.log("x-y", this.canvasPos)
    },
    //5.点击上一张跟下一张时重新渲染pdf画布
    queueRenderPage(num) {
      if (this.pageRendering) {
        this.pageNumPending = num
      } else {
        this.renderPage(num)
      }
    },
    //切换页码refresh更新印章画布，清空画布并将有印章的页码数据重新渲染
    refreshFabricCanvas() {
      //清空画布,并重新渲染图章
      this.fabricCanvas.clear();
      // 渲染stampList中记录的印章
      console.log("清空pdf画布", this.stampList)
      let stamps = this.stampList[this.pdfPage]
      if (stamps) {
        for (let attr in stamps) {
          let { imgurl, x, y, id } = stamps[attr]
          this.renderStamp(imgurl, { x, y, id })
        }
      }
    },//下一页
    handleNext() {
      if (this.pdfPage >= this.pageNum) {
        return;
      }
      this.pdfPage++
      this.refreshFabricCanvas()
      this.queueRenderPage(this.pdfPage)
    },//上一页
    handlePrev() {
      if (this.pdfPage <= 1) {
        return;
      }
      this.pdfPage--
      this.refreshFabricCanvas()
      this.queueRenderPage(this.pdfPage)
    },
    handleDrag(url) {
      this.stempImgUrl = url
    },
    handleDrop(ev) {
      console.log("放置图片", ev);
      console.log(this.stempImgUrl);
      // 创建 Fabric.js 图片对象
      fabric.Image.fromURL(this.stempImgUrl, (img) => {
        console.log("img", img)
        // 设置图片的位置和大小
        const scaleFactor = Math.min(100 / img.width, 100 / img.height);
        //client鼠标最后记录的位置-画布距离边界的位置
        let left = (ev.clientX - this.canvasPos.x - 50) / this.scale;
        let top = (ev.clientY - this.canvasPos.y - 50) / this.scale;
        let id = "uId" + Math.random();
        // img.set(id, id)
        img.scale(scaleFactor * this.scale);
        img.set({
          left,
          top,
          selectable: true,
          id,
        });
        let imgurl = this.stempImgUrl
        console.log("tupian", img)
        // 初次记录当前印章与pdf页码及其坐标位置对应关系★
        //如果印章列表有数据就是做更新，没有就是添加
        if (this.stampList[this.pdfPage]) {
          this.stampList[this.pdfPage][id] = {
            imgurl,
            x: left,
            y: top,
            id
          };
        } else {
          this.stampList[this.pdfPage] = {
            [id]: {
              imgurl,
              x: left,
              y: top,
            },
          };
        }
        // let imgurl = this.stempImgUrl;
        this.fabricCanvas.add(img);

        //在当前页面拖动印章做实时更新操作
        this.fabricCanvas.on("mouse:up", (options) => {
          console.log("mouse事件", options)
          let { target, pointer } = options
          if (target) {
            let { x, y } = pointer
            this.stampList[this.pdfPage][target.id].x = x;
            this.stampList[this.pdfPage][target.id].y = y
            console.log("拖动的印章", this.stampList[this.pdfPage][target.id])
          }
        })
      })
    },
    renderStamp(imgurl, { x = 0, y = 0, id }) {
      fabric.Image.fromURL(imgurl, (img) => {
        // 设置图片的位置和大小
        const scaleFactor = Math.min(100 / img.width, 100 / img.height);
        // Scale the image down
        const scale = this.scale;

        // 设置图片的位置和大小
        img.scale(scaleFactor * scale);
        img.set({
          left: x,
          top: y,
          selectable: true,
          id
        });
        // 将图片添加到 Canvas
        this.fabricCanvas.add(img);

        //实时更新stampList中对应图章位置
        this.fabricCanvas.on("mouse:up", (options) => {
          console.log("mouse:up事件：", options);
          let { target, pointer } = options;
          if (target) {
            let { x, y } = pointer;
            this.stampList[this.pageNum][target.id].x = x;
            this.stampList[this.pageNum][target.id].y = y;
            console.log(this.stampList);
          }
        });
      });
    }
  }
}
</script>

<style lang="scss" scoped>
.imgItem {
  width: 50px;
  height: 50px;
}

.box-con {
  width: 400px;
  height: 300px;
  position: relative;
  // margin: 100px auto;
  // padding: 10px;
  // margin: 10px;

  .stamp-canvas {
    height: 100%;
    width: 100%;
    position: absolute;
    z-index: 10;
    top: 0;
    left: 0;
  }

  .pdf-canvas {
    height: 100%;
    width: 100%;
    border: 1px solid red;
  }

}
</style>